/**
 * Title : jFamilyTree
 * Description: jFamilyTree
 * About: Designed and rewritten by Andreas Lundgren, based on the Treebolic application suite
 * Copyright:	(c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.commons;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Class searcher
 * 
 * @author Walter Angerer 'greenhorn'
 * @author Bernard Bou
 */
public class Searcher
{
	/**
	 * Find classes matching pattern in classpath
	 * 
	 * @param thisPattern
	 *        pattern
	 * @return result set
	 */
	static public Set<String> findClasses(String thisPattern)
	{
		Set<String> thisList = new TreeSet<String>();
		char thisSeparator = File.separatorChar;
		thisPattern = thisPattern.replaceAll("\\\\.", thisSeparator ==  '/' ? "/" : "\\\\\\\\"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		for (String thisFileName : findFiles(thisPattern + "\\.class$").keySet()) //$NON-NLS-1$
			thisList.add(thisFileName.replace(thisSeparator, '.').substring(0, thisFileName.length() - 6));
		return thisList;
	}

	/**
	 * Find files matching pattern in classpath
	 * 
	 * @param thisPattern
	 *        pattern
	 * @return result set
	 */
	static public Map<String, String> findFiles(String thisPattern)
	{
		Map<String, String> thisList = new TreeMap<String, String>();
		String thisClassPath = System.getProperty("java.class.path"); //$NON-NLS-1$
		String[] thesePathElements = thisClassPath.split(File.pathSeparator);
		for (String thisPathElement : thesePathElements)
		{
			try
			{
				File thisFile = new File(thisPathElement);
				if (thisFile.isDirectory())
					thisList.putAll(findInDirectory(thisFile, thisPattern, thisPathElement));
				else
					thisList.putAll(findInFile(thisFile, thisPattern));
			}
			catch (IOException e)
			{
				System.err.println("Find files: " + e.toString()); //$NON-NLS-1$
			}
		}
		return thisList;
	}

	/**
	 * Find in JAR file
	 * 
	 * @param thisFile
	 *        JAR file
	 * @param thisPattern
	 *        pattern
	 * @return file with matching file path
	 * @throws IOException
	 */
	static private Map<String, String> findInFile(File thisFile, String thisPattern) throws IOException
	{
		Map<String, String> thisMap = new TreeMap<String, String>();
		if (thisFile.canRead() && thisFile.getAbsolutePath().endsWith(".jar")) //$NON-NLS-1$
		{
			JarFile thisJar = new JarFile(thisFile);
			for (Enumeration<JarEntry> theseEntries = thisJar.entries(); theseEntries.hasMoreElements();)
			{
				JarEntry thisEntry = theseEntries.nextElement();
				if (thisEntry.getName().matches(thisPattern))
				{
					thisMap.put(thisEntry.getName(), thisJar.getName());
				}
			}
		}
		return thisMap;
	}

	/**
	 * Find in directory
	 * 
	 * @param thisDirectory
	 *        directory
	 * @param thisPattern
	 *        pattern
	 * @return file with matching file path
	 * @throws IOException
	 */
	static private Map<String, String> findInDirectory(File thisDirectory, String thisPattern, String thisPathElement) throws IOException
	{
		Map<String, String> thisMap = new TreeMap<String, String>();
		for (File thisDirectoryEntry : thisDirectory.listFiles())
		{
			if (thisDirectoryEntry.getAbsolutePath().matches(thisPattern))
			{
				String thisFileName = thisDirectoryEntry.getAbsolutePath().substring(thisPathElement.length() + 1);
				thisMap.put(thisFileName, thisPathElement);
			}
			else if (thisDirectoryEntry.isDirectory())
				thisMap.putAll(findInDirectory(thisDirectoryEntry, thisPattern, thisPathElement));
			else
				thisMap.putAll(findInFile(thisDirectoryEntry, thisPattern));
		}
		return thisMap;
	}

	/**
	 * Find files matching pattern in classpath
	 * 
	 * @param thisPattern
	 *        pattern
	 * @return list of urls
	 */
	static public List<String> findFileUrls(String thisPattern)
	{
		return toUrls(findFiles(thisPattern));
	}

	/**
	 * Convert file map to list of urls
	 * 
	 * @param thisMap
	 *        path to container map
	 * @return list of urls
	 */
	static public List<String> toUrls(Map<String, String> thisMap)
	{
		List<String> theseUrls = new ArrayList<String>();
		for (Map.Entry<String, String> thisEntry : thisMap.entrySet())
		{
			String thisContainer = thisEntry.getValue();
			String thisPath = thisEntry.getKey();
			String thisUrlString = null;
			if (thisContainer.endsWith(".jar")) //$NON-NLS-1$
				thisUrlString = "jar:file:" + thisContainer + "!/" + thisPath; //$NON-NLS-1$ //$NON-NLS-2$
			else
				thisUrlString = "file:" + thisContainer + '/' + thisPath; //$NON-NLS-1$
			theseUrls.add(thisUrlString);
		}
		return theseUrls;
	}

	/**
	 * Main
	 * 
	 * @param args
	 *        args
	 */
	public static void main(String[] args)
	{
		for (String thisClassName : findClasses(".*Provider")) //$NON-NLS-1$
			System.out.println("PROVIDER CLASS: " + thisClassName); //$NON-NLS-1$

		for (Map.Entry<String, String> thisEntry : findFiles(".*xsl$").entrySet()) //$NON-NLS-1$
			System.out.println("XSL FILE: " + thisEntry.getKey() + " @ " + thisEntry.getValue()); //$NON-NLS-1$ //$NON-NLS-2$
	}
}
